package com.dream.libxrec.player;

import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.util.Log;

import java.nio.ByteBuffer;

/**
 * 获取H264或H265的裸数据
 */
public class Decoder {

    private static final String TAG = "VideoDecoder";
    private volatile boolean mStop = true;
    private volatile boolean isPause;//暂停解码
    private volatile boolean isSeeking = false;
    private volatile long mSeekPts = -1L;
    private final Object mPauseLock = new Object();
    private final Object mEndLock = new Object();
    //读取到文件结尾等待进一步操作（退出或seek等）的锁，避免直接释放了资源
    private MediaExtractor mMediaExtractor = null;
    private IDecodeCallback mCB = null;

    private MediaFormat mMediaFormat;
    private long mDurationUs;//视频轨时长，单位为微秒
    private boolean isEndRelease = false;//读到文件结尾时自动释放资源

    public boolean init(String path,
                        IDecodeCallback mCB) throws Exception{
        this.mCB = mCB;
        this.mMediaExtractor = new MediaExtractor();
        this.mMediaExtractor.setDataSource(path);
        int videoTrackIndex = selectVideoTrack(mMediaExtractor);
        if (videoTrackIndex < 0) {
            Log.e(TAG, "No video track found in " + path);
            return false;
        }

        MediaFormat mediaFormat = mMediaExtractor.getTrackFormat(videoTrackIndex);
        this.mMediaFormat = mediaFormat;
        try {
            this.mDurationUs = mediaFormat.getLong(MediaFormat.KEY_DURATION);
        }catch (Exception e){
            this.mDurationUs = -1;
        }

        if (this.mCB != null){
            this.mCB.init(mediaFormat);
        }

        return true;
    }

    public void start() throws Exception{
        mStop = false;

        ByteBuffer inputBuffer = ByteBuffer.allocate(100 * 1024);

        while (!mStop) {
            //extractor.getSampleSize可以获取帧大小
            int size = mMediaExtractor.readSampleData(inputBuffer, 0);
            if (size < 0) {
                if (isEndRelease){
                    // 读取到文件末尾,结束循環
                    break;
                }else{
                    synchronized (mEndLock){
                        try {
                            mEndLock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    continue;
                }
            }

            long pts = mMediaExtractor.getSampleTime();

            if (mCB != null&&!isPause&&!isSeeking){
                byte[] data = new byte[size];
                inputBuffer.get(data,0,size);
                mCB.onDecode(data,pts);
            }

            if (isPause&&!mStop){
                synchronized (mPauseLock){
                    try {
                        mPauseLock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

            if (isSeeking && mSeekPts >= 0){
                mMediaExtractor.seekTo(mSeekPts,
                        MediaExtractor.SEEK_TO_CLOSEST_SYNC);

                Log.i(TAG, "video seekTo: " + mSeekPts);
                isSeeking = false;
                mSeekPts = -1;
            }

            mMediaExtractor.advance();
        }

        release();
    }

    public void onPause(boolean isPause){
        this.isPause = isPause;
        if (!isPause){
            synchronized (mPauseLock){
                mPauseLock.notifyAll();
            }
        }
    }

    public void close(){
        if (!mStop){
            mStop = true;
            if (isPause){
                isPause = false;
                synchronized (mPauseLock){
                    mPauseLock.notifyAll();
                }
            }

            synchronized (mEndLock){
                mEndLock.notify();
            }
        }else{
            release();
        }
    }

    private void release(){
        mStop = true;

        mMediaFormat = null;

        if (mMediaExtractor != null){
            try {
                mMediaExtractor.release();
            }catch (Exception ignored){}

            mMediaExtractor = null;
        }

        Log.i(TAG,"VideoDecoder release finish");
    }

    public MediaFormat getFormat(){
        return mMediaFormat;
    }

    private static int selectVideoTrack(MediaExtractor mMediaExtractor) {
        int numTracks = mMediaExtractor.getTrackCount();
        for (int i = 0; i < numTracks; i++) {
            MediaFormat format = mMediaExtractor.getTrackFormat(i);
            String mime = format.getString(MediaFormat.KEY_MIME);
            if (mime!=null&&mime.startsWith("video/")) {
                mMediaExtractor.selectTrack(i);
                return i;
            }
        }
        return -1;
    }

    public long getDurationUs(){
        return mDurationUs;
    }

    public void seekTo(long pts) {
        if (mMediaExtractor == null){
            Log.i(TAG,"video media extractor is release");
            return;
        }

        isSeeking = true;
        mSeekPts = pts;

        synchronized (mEndLock){
            mEndLock.notifyAll();
        }

        synchronized (mPauseLock){
            mPauseLock.notifyAll();
        }
    }

    /**
     * 是否在文件结尾时自动释放资源（如果为true，播放结束后会自动释放，但无法再进行seek），默认为false
     * @param isEndRelease
     */
    public void isEndRelease(boolean isEndRelease){
        this.isEndRelease = isEndRelease;
    }

    public boolean isSeeking() {
        return isSeeking;
    }
}
